home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcr / pcr4_4.lha / DIST / gc / GCVirtualDirty.c < prev    next >
C/C++ Source or Header  |  1991-09-24  |  35KB  |  1,128 lines

  1. /* 
  2.     Virtual dirty bit implementation to assist the incremental garbage 
  3.     collector.  This exercises UNIX facilities that are customarily
  4.     at least partially broken.  This version is tuned for SunOS on SPARCs,
  5.     and the corresponding bug collection.  In particular, we assume:
  6.     
  7.     1) Returning from a segmentation violation handler properly restarts
  8.        the instruction.
  9.        
  10.     2) System calls cannot be correctly restarted from a segmentation violation.
  11.     
  12.     Point (1) is known not to hold for at least some versions of SunOS on
  13.     Sun 3s.  This code is therefore not portable to these machines.  This code
  14.     would be much simpler if it were not for point (2).  This is all terribly
  15.     inefficient in comparison to an implementation inside the pager.
  16.     
  17.     Boehm, January 2, 1991 5:54:12 pm PST
  18. */
  19.  
  20. #define DEBUG    
  21. #include "xr/GCVirtualDirty.h"
  22. #include "xr/Threads.h"
  23. #include "xr/ThreadsSchedCtl.h"
  24. #include "xr/ThreadsMsg.h"
  25. #include "xr/UIO.h"
  26. #include "xr/GC.h"
  27. #include "xr/GCPrivate.h"  /* We need MAP_SIZE, the maximum number of */
  28.                /* pages in the heap.                      */                
  29. #include <sys/signal.h>
  30. #ifdef DIRTY_BITS
  31. #   include <sys/mman.h>
  32. #endif
  33. #ifdef IBMRS6000
  34. #   include "xr/ThreadsSharedMemPrivate.h"
  35.             /* We need start of system memory */
  36. #endif
  37. #include <errno.h>
  38.  
  39. # ifdef DIRTY_BITS
  40.  
  41. # define UNPROTECT_STATS
  42. # ifdef UNPROTECT_STATS
  43.   extern int XR_explicitly_unprotected_pages;
  44.           /* Number of times XR_PrepareForWriting was successful.   */
  45.   extern int XR_wasted_preparation_calls;
  46.         /* Number of times XR_PrepareForWriting was unsuccessful. */
  47.   extern int XR_protection_violations;     
  48. # endif
  49.  
  50.  
  51. #define VD_CLIENT_NO 1    /* Sequence number for memory protection clients */
  52.  
  53. #define I_HOLD_ML(ml) (((XR_ML)(ml))->ml_holder == XR_currThread)
  54.  
  55. typedef char VDS;
  56. # define VDS_CLEAN 0  /* ==> page is currently clean and protected */
  57. # define VDS_DIRTY 1  /* ==> page known to be dirty, may or may not */
  58.                /*     be protected.                  */
  59. # define VDS_UNINIT 1 /* ==> All pages are initially dirty. */
  60.  
  61.  
  62. VDS VD_Status[VDS_SIZE];/* Should be static, but we want it allocated with */
  63.               /* other global GC variables, so the collector     */
  64.               /* doesn't scan it, and it's never protected.         */
  65.               /* An index of 0 corresponds to VD_base.         */
  66.  
  67. typedef char ClientSet;
  68.  
  69. ClientSet VD_Clients[VDS_SIZE];  /* Should be static ... */
  70.  
  71. /* Bit vectors representing clients that need the page */
  72. /* protected.                           */
  73. # define VDS_CLIENT_MASK(n) (1 << (n))
  74. # define VDS_HAS_CLIENTS(i) (VD_Clients[i] != 0)
  75. # define VDS_HAS_CLIENT(i,n) ((VD_Clients[i] & VDS_CLIENT_MASK(n)) != 0)
  76. # define VDS_ADD_CLIENT(i,n) (VD_Clients[i] |= VDS_CLIENT_MASK(n))
  77. # define VDS_DELETE_CLIENT(i,n) (VD_Clients[i] &= ~VDS_CLIENT_MASK(n))
  78.  
  79. # endif DIRTY_BITS
  80.  
  81. # define GARBAGE 93      /* Not a plausible page size. */
  82. static int realPageSize = GARBAGE;  /* Physical page size on this processor. */
  83. static int maxPageSize = GARBAGE;   /* Max of above and VD_PAGE_SIZE.        */
  84.  
  85. /* Lock for updating virtual dirty bits.*/
  86. /* Must be held when write-protecting pages.  Also held while       */
  87. /* VD_Status is inconsistent (e.g. in system call wrapper).          */
  88. /* Also acquired while registering protection handlers.            */
  89. static struct XR_MLRep vDWrite_ml;
  90. XR_Thread XR_holderOfVDWriteLock = NIL;
  91.  
  92. void XR_AcquireVDWriteLock()
  93. {
  94.     XR_MonitorEntry(&vDWrite_ml);
  95.     if( XR_holderOfVDWriteLock != NIL )
  96.         XR_Panic("AcquireDWriteLock 0");
  97.     XR_holderOfVDWriteLock = XR_currThread;
  98. }   
  99.  
  100. void XR_ReleaseVDWriteLock()
  101. {
  102.     if( XR_holderOfVDWriteLock != XR_currThread )
  103.         XR_Panic("ReleaseVDWriteLock 0");
  104.     XR_holderOfVDWriteLock = NIL;
  105.     XR_MonitorExit(&vDWrite_ml);
  106. }
  107.  
  108. unsigned XR_GetVDPageSize()
  109. {
  110.     return(VD_PAGE_SIZE);
  111. }
  112.  
  113. # ifdef DIRTY_BITS
  114.  
  115. # define INDX_FROM_ADDR(addr) \
  116.            BYTES_TO_PAGES((addr) - ((XR_Pointer)VD_base))
  117.            
  118. # define LOW_PAGE(seg) INDX_FROM_ADDR((seg).seg_addr)
  119.  
  120. # define HIGH_PAGE(seg) INDX_FROM_ADDR((seg).seg_addr + (seg).seg_bytes - 1)
  121.  
  122. /* Unprotect a range in this VP, and record the page as having been */
  123. /* unprotected.  This is an assertion by this client that the page  */
  124. /* no longer needs to be protected by this client in any VP.        */
  125. /* Short term unprotection should be done with mprotect and         */
  126. /* inhibiting preemption.                        */
  127. int /* 0 or -errno */
  128. XR_UnprotectHeap(addr, len, client)
  129. XR_Pointer addr;
  130. unsigned long len;
  131. int client;
  132. {
  133.     register int i;
  134.     register unsigned limit = INDX_FROM_ADDR((unsigned long) addr + len - 1);
  135.     
  136.     for (i = INDX_FROM_ADDR(addr); i <= limit; i++) {
  137.         VDS_DELETE_CLIENT(i, client);
  138.     }
  139.     /* mprotect should be done last, so a concurrent XR_Reprotect won't */
  140.     /* undo it.                                */
  141.     if (mprotect(addr, len,
  142.                  PROT_READ | PROT_EXEC | PROT_WRITE) != 0) {
  143.         return(-XR_GetErrno());
  144.     }
  145.  
  146.     return(0);
  147. }
  148.  
  149. /* Or the value x into each character in the range [addr, addr+len) */
  150. void byte_or(addr, len, x)
  151. register char * addr;
  152. register long len;
  153. char x;
  154. {
  155. #   define BO_THRESHOLD 8
  156.     register unsigned long *p;
  157.     register unsigned long *lim2;
  158.     register char * q;
  159.     register char *lim1;
  160.     register char * lim3;
  161.     register int i;
  162.     register unsigned long mask;
  163.     
  164.     if (len < BO_THRESHOLD) {
  165.         for (q = addr; q < addr +len; q++) {
  166.             *q |= x;
  167.         }
  168.     } else {
  169.         mask = x;
  170.         for (i = 1; i < BYTES_PER_WORD; i++) mask |= (mask << 8);
  171.         lim1 = (char *)
  172.                WORDS_TO_BYTES(BYTES_TO_WORDS(((long)addr) + BYTES_PER_WORD-1));
  173.         lim2 = (word *) WORDS_TO_BYTES(BYTES_TO_WORDS(((long)addr)  + len));
  174.         lim3 = addr + len;
  175.         for (q = addr; q < lim1; q++) {
  176.             *q |= x;
  177.         }
  178.         for (p = (unsigned long *)lim1; p < lim2; p++) {
  179.             *p |= mask;
  180.         }
  181.         for (q = (char*)lim2; q < lim3; q++) {
  182.             *q |= x;
  183.         }
  184.     }
  185. }
  186.  
  187. /* Interval containing VD data structures that should not be protected.      */
  188. static XR_Pointer start_unprotected;
  189. static XR_Pointer end_unprotected;
  190.  
  191. /* Inverse of XR_UnProtectHeap.  Protection affects all VPs         */
  192. /* Pages entirely in the range [&GC_first_global, &GC_last_global] are     */
  193. /* implicitly excluded, to avoid infinite recursion.            */
  194. /* This requires that all globals accessed by the signal handler be     */
  195. /* sufficently near the middle of this range that they do not get    */
  196. /* protected.                                */
  197. int /* 0 or -errno */
  198. XR_ProtectHeap(addr, len, client)
  199. XR_Pointer addr;
  200. unsigned long len;
  201. int client;
  202. {
  203.     register int i;
  204.     register unsigned limit = INDX_FROM_ADDR((unsigned long) addr + len - 1);
  205.     register char client_mask = VDS_CLIENT_MASK(client);
  206.   
  207.     if ( addr < start_unprotected && addr + len > end_unprotected) {
  208.         int result = XR_ProtectHeap(addr, start_unprotected - addr, client);
  209.             
  210.         if (result < 0) return(result);
  211.         return(XR_ProtectHeap(end_unprotected,
  212.                           addr+len - end_unprotected, client));
  213.     }
  214.     if ( addr < start_unprotected && addr + len > start_unprotected ) {
  215.         len = start_unprotected - addr;
  216.     }
  217.     if ( addr < end_unprotected && addr + len > end_unprotected ) {
  218.         len = addr + len - end_unprotected;
  219.         addr = end_unprotected;
  220.     }
  221.     if (!I_HOLD_ML(&vDWrite_ml)) {
  222.         XR_Panic("XR_ProtectHeap");
  223.     }
  224.     i = INDX_FROM_ADDR(addr);
  225.     byte_or(VD_Clients+i, limit+1 - i, client_mask);
  226.     if (XR_MProtect4(addr, len,
  227.                      PROT_READ | PROT_EXEC,
  228.                      FALSE /* VPs only */)  != 0) {
  229.         return(-XR_GetErrno());
  230.     }
  231. }
  232.  
  233. /* Protect all pages in the given range that have clients needing this */
  234. /* protection.  Used after temporarily unprotecting memory in a single */
  235. /* VP.                                       */
  236. static void
  237. Reprotect(addr, len)
  238. XR_Pointer addr;
  239. unsigned long len;
  240. {
  241.     register int i;
  242.     register unsigned limit = INDX_FROM_ADDR((unsigned long) addr + len - 1);
  243.     register bool need_mprotect = FALSE;
  244.     register XR_Pointer prot_start;
  245.     register unsigned long prot_len;
  246.  
  247.     if (!I_HOLD_ML(&vDWrite_ml)) {
  248.         XR_Panic("Reprotect");
  249.     }
  250.     for (i = INDX_FROM_ADDR(addr); i <= limit; i++) {
  251.         if (need_mprotect) {
  252.             if (VDS_HAS_CLIENTS(i)) {
  253.                 prot_len += VD_PAGE_SIZE;
  254.             }
  255.         } else {
  256.             if (VDS_HAS_CLIENTS(i)) {
  257.                 need_mprotect = TRUE;
  258.                 prot_start = (XR_Pointer)(
  259.                           (unsigned long)VD_base + PAGES_TO_BYTES(i));
  260.                 prot_len = VD_PAGE_SIZE;
  261.             }
  262.         }
  263.         if (need_mprotect && (i == limit || !VDS_HAS_CLIENTS(i))) {
  264.             if (mprotect(prot_start, prot_len,
  265.                          PROT_READ | PROT_EXEC) != 0) {
  266.                 XR_ConsoleMsg("Reprotect: addr = 0x%X, len = 0x%X\n",
  267.                           prot_start, prot_len);
  268.                 XR_Panic("Reprotect: mprotect failed");
  269.             }
  270.         need_mprotect = FALSE;
  271.         }
  272.     }
  273. }
  274.  
  275.  
  276. /* Unprotect all pages in the given range that no longer have clients. */
  277. /* This is done in all VPs, so that it becomes safe to remove a        */
  278. /* protection handler.    This is EXPENSIVE if many small segments need  */
  279. /* to be unprotected.                                     */
  280. static void
  281. ResetProtection(addr, len)
  282. XR_Pointer addr;
  283. unsigned long len;
  284. {
  285.     register int i;
  286.     register unsigned limit = INDX_FROM_ADDR((unsigned long) addr + len - 1);
  287.     register bool need_mprotect = FALSE;
  288.     register XR_Pointer unprot_start;
  289.     register unsigned long unprot_len;
  290.  
  291.     if (!I_HOLD_ML(&vDWrite_ml)) {
  292.         XR_Panic("Reprotect");
  293.     }
  294.     for (i = INDX_FROM_ADDR(addr); i <= limit; i++) {
  295.         if (need_mprotect) {
  296.             if (!VDS_HAS_CLIENTS(i)) {
  297.                 unprot_len += VD_PAGE_SIZE;
  298.             }
  299.         } else {
  300.             if (!VDS_HAS_CLIENTS(i)) {
  301.                 need_mprotect = TRUE;
  302.                 unprot_start = (XR_Pointer)(
  303.                           (unsigned long)VD_base + PAGES_TO_BYTES(i));
  304.                 unprot_len = VD_PAGE_SIZE;
  305.             }
  306.         }
  307.         if (need_mprotect && (i == limit || VDS_HAS_CLIENTS(i))) {
  308.             if (XR_MProtect4(unprot_start, unprot_len,
  309.                              PROT_READ | PROT_EXEC | PROT_WRITE,
  310.                              FALSE /* VPs only */) != 0) {
  311.                 XR_ConsoleMsg("ResetProtection: addr = 0x%X, len = 0x%X\n",
  312.                           unprot_start, unprot_len);
  313.                 XR_Panic("ResetProtection: XR_MProtect4 failed");
  314.             }
  315.         need_mprotect = FALSE;
  316.         }
  317.     }
  318. }
  319.  
  320. /* Descriptor for memory region and buffer currently being processed.       */
  321. /* References to this region must be handled differently by signal handler. */
  322. /* volatile */ XR_VDBuf vDCurrentBuf;  /* should be static */
  323.  
  324. /* Memory write fault handler.  Scp is not used. */
  325. static bool ProtVHandler(scp, orig_addr)
  326. struct sigcontext *scp;
  327. XR_Pointer orig_addr;
  328. {
  329.     register unsigned indx;
  330.     register XR_VDBuf MyBuf;
  331.     register unsigned current_indx;
  332.     register unsigned lim_indx;
  333.     register XR_Pointer addr;
  334.     register int result;
  335.     
  336.     /* maxPageSize align addr */
  337.         addr = orig_addr & ~(maxPageSize-1);
  338.         
  339.     if (addr < (XR_Pointer)VD_base) {
  340.         return(FALSE);
  341.     }
  342.  
  343.     if (addr >= (XR_Pointer)GC_heaplim) {
  344.         return(FALSE);
  345.     }
  346. #   ifdef UNPROTECT_STATS
  347.     XR_protection_violations++;
  348. #   endif
  349.  
  350.     indx = INDX_FROM_ADDR(addr);
  351.  
  352.     /* Unprotect this page.  This is done only on this VP.  It could */
  353.     /* conceivably be undone by VDSetClean before we return.  If so, */
  354.     /* we simply reenter the handler, and try again ...             */
  355.     /* Since this is a sgnal handler, and the last action of XR_VDOp */
  356.     /* involves sending signals, there is a clearly defined order    */
  357.     /* between this and VDSetCleanAndGet calls.  If this handler     */
  358.     /* finishes before a VDSetCleanAndGet call,              */
  359.     /* then either the subsequent write will                   */
  360.     /* also finish before the call, or it will be seen by a          */
  361.     /* subsequent call, after it generates another fault.  Thus a    */
  362.     /* dirty bit will be seen after the write fault occurred.        */
  363.     /* We unprotect only a physical page, since we might otherwise   */
  364.     /* interfere with data breakpoints and the like.             */
  365.           if ((result = XR_UnprotectHeap(orig_addr & ~(realPageSize-1),
  366.                               realPageSize, VD_CLIENT_NO)) != 0) {
  367.             /* We're in serious trouble.  Based on examination of the */
  368.             /* code, XR_ConsoleMsg writes only on the stack, so this  */
  369.             /* should be safe, no matter what's write protected.      */
  370.             XR_ConsoleMsg(
  371.               "Mprotect failure in write fault handler %d(a %d, s %d)\n",
  372.               -result, VD_base + PAGES_TO_BYTES(indx), realPageSize);
  373.             return(FALSE);
  374.           } 
  375.     
  376.     /* Set the appropriate dirty bits.  They may be somewhere other          */
  377.     /* than in VD_Status, if the dirty bit is currently being copied to a    */
  378.     /* user buffer.                                 */
  379.       lim_indx = indx + maxPageSize/VD_PAGE_SIZE;
  380.       for (current_indx = indx; indx < lim_indx; indx++) {
  381.         while ((MyBuf = vDCurrentBuf) == NIL && VD_Status[indx] != VDS_DIRTY) {
  382.           /* The loop body will be executed an average of just over once.    */
  383.           /* It can never be executed more than one additional time as a     */
  384.           /* result of  a simultaneous VDSetCleanAndGet call, since such     */
  385.           /* calls will block at the end until we return.  VDSetClean calls  */
  386.           /* could prevent us from making progress if the UNIX scheduler     */
  387.           /* really conspired against us.                     */
  388.           VD_Status[indx] = VDS_DIRTY;
  389.         }
  390.         if (MyBuf != NIL) {
  391.           char * vdb_bufp = MyBuf -> vdb_buf;
  392.           unsigned start_page = LOW_PAGE(MyBuf->vdb_seg);
  393.           
  394.           if (indx >= start_page && indx <= HIGH_PAGE(MyBuf->vdb_seg)) {
  395.               vdb_bufp[indx - start_page] = VDS_DIRTY;
  396.           } else {
  397.               VD_Status[indx] = VDS_DIRTY;
  398.           }
  399.         } else {
  400.           /* We set the bit, then vDCurrentBuf was NIL, then we saw the bit  */
  401.           /* still set.  This means we either got in before a simultaneous   */
  402.           /* VDSetCleanAndGet changed vDCurrentBuf (and thus we're safe), or */
  403.           /* vDCurrentBuf was changed back in the meantime, and the bit in   */
  404.           /* question hasn't been clobbered during the clearing phase (and   */
  405.           /* thus we again know that the dirty bit was seen).  Admittedly,   */
  406.           /* this is all rather convoluted, but the code is simple and fast. */
  407.         }
  408.       }
  409.       
  410.     return(TRUE);           
  411. }
  412.  
  413.  
  414. /* Indicate that addr is about to be written. Called when a write access  */
  415. /* is likely to generate a fault, in order to avoid the signal handling   */
  416. /* overhead.  This is a noop unless we can determine with certainty that  */
  417. /* that a write fault would happen.                      */
  418. /* Should not be called on an address that has been temporarily         */
  419. /* unprotected for a system call.                      */
  420. /* The specified interval is assumed to be in the heap.               */
  421. void XR_PrepareForWriting(orig_addr)
  422. XR_Pointer orig_addr;
  423. {
  424.     register XR_Pointer addr = orig_addr & ~(maxPageSize-1);
  425.     register int i = INDX_FROM_ADDR(addr);
  426.     register int result;
  427.     
  428.     if (XR_numberOfProtVClients == 1 && VDS_HAS_CLIENT(i, VD_CLIENT_NO)) {
  429.         register bool acquired_lock = FALSE;
  430.         register int lim = i + maxPageSize/VD_PAGE_SIZE;
  431.         register int j;
  432.         
  433. #    ifdef UNPROTECT_STATS
  434.          XR_explicitly_unprotected_pages++;
  435. #    endif         
  436.         if (XR_holderOfVDWriteLock != XR_currThread) {
  437.             XR_AcquireVDWriteLock();
  438.             acquired_lock = TRUE;
  439.         }
  440.         
  441.         for (j = i; j < lim; j++) {
  442.              VD_Status[j] = VDS_DIRTY;
  443.         }
  444.         if ((result = XR_UnprotectHeap(addr,
  445.                              maxPageSize, VD_CLIENT_NO)) != 0) {
  446.             XR_ConsoleMsg(
  447.               "Mprotect failure in XR_PrepareForWriting %d(a %d, s %d)\n",
  448.               -result, VD_base + PAGES_TO_BYTES(i), maxPageSize);
  449.             XR_Panic("XR_PrepareForWriting 0");
  450.         } 
  451.     if (acquired_lock) {
  452.             XR_ReleaseVDWriteLock();
  453.         }
  454.     } else {
  455. #    ifdef UNPROTECT_STATS
  456.          XR_wasted_preparation_calls++;
  457. #    endif 
  458.     }
  459. }
  460.  
  461. static bool vDStarted = FALSE;  /* Protected by vDWrite_ml */
  462.  
  463. /* Start up virtual dirty bit handling. */
  464. void XR_StartVD()
  465. {
  466.     int i;
  467.     bool acquired_lock = FALSE;
  468.  
  469.     if (vDStarted) return;
  470.     if (XR_holderOfVDWriteLock != XR_currThread) {
  471.             XR_AcquireVDWriteLock();
  472.             acquired_lock = TRUE;
  473.     }
  474.     XR_NormalVMsg "Starting virtual dirty bit handling for %d pages\n",
  475.                VDS_SIZE);
  476.     realPageSize = XR_GetPageSize();
  477.     if (realPageSize > VD_PAGE_SIZE) {
  478.         maxPageSize = realPageSize;
  479.     } else {
  480.         maxPageSize = VD_PAGE_SIZE;
  481.     }
  482.     if (VD_base == 0) {
  483. #       ifdef IBMRS6000
  484.           VD_base = (char *)(XR_memInfo->mi_sysMemStart);
  485. #       else
  486.           VD_base = (char *)(XR_sysArea -> sa_sharedDataSeg.seg_addr);
  487. #       endif
  488.     }
  489.     VD_base += maxPageSize-1;
  490.     VD_base = (char *)((unsigned long) VD_base & ~(maxPageSize-1));
  491.     start_unprotected = (((long)(&GC_first_global))+maxPageSize-1)
  492.                 & ~(maxPageSize-1);
  493.     end_unprotected = ((long)(&GC_last_global)) & ~(maxPageSize-1);
  494.     for (i = 0; i < VDS_SIZE; i++) {
  495.         VD_Status[i] = VDS_UNINIT;
  496.     }
  497.     XR_RegisterProtVHandler(VD_CLIENT_NO
  498.                      /* data breakpoint handler should run first */,
  499.                             ProtVHandler, 0, 0, 0);
  500.     if (GC_heapstart - VD_base > MAX_DATA_SIZE) {
  501.         GC_iprintf("Warning - data segment larger than expected\n");
  502.     }
  503.     vDStarted = TRUE;
  504.     if (acquired_lock) {
  505.         XR_ReleaseVDWriteLock();
  506.     }
  507. }
  508.  
  509. /* End virtual dirty bit handling.                     */
  510. /* This is always safe.  Everything will appear dirty to the next    */
  511. /* request.                                */
  512. void XR_EndVD()
  513. {
  514.     bool acquired_lock = FALSE;
  515.     register int i;
  516.     
  517.     XR_NormalVMsg "Stopping virtual dirty bit handling\n");
  518.     if (XR_holderOfVDWriteLock != XR_currThread) {
  519.             XR_AcquireVDWriteLock();
  520.             acquired_lock = TRUE;
  521.     }
  522.     for (i = 0; i < VDS_SIZE; i++) {
  523.         VD_Status[i] = VDS_UNINIT;
  524.         VDS_DELETE_CLIENT(i,VD_CLIENT_NO);
  525.     }
  526.     ResetProtection(VD_base, GC_heaplim - VD_base);
  527.     XR_RegisterProtVHandler(VD_CLIENT_NO, 0, 0, 0, 0);
  528.     vDStarted = FALSE;
  529.     if (acquired_lock) {
  530.         XR_ReleaseVDWriteLock();
  531.     }
  532. }
  533.  
  534. # endif DIRTY_BITS
  535. # ifndef DIRTY_BITS
  536. void XR_StartVD()
  537. {
  538.     realPageSize = XR_GetPageSize();
  539.     if (realPageSize > VD_PAGE_SIZE) {
  540.         maxPageSize = realPageSize;
  541.     } else {
  542.         maxPageSize = VD_PAGE_SIZE;
  543.     }
  544.     if (VD_base == 0) {
  545. #       ifdef IBMRS6000
  546.           VD_base = (char *)(XR_memInfo->mi_sysMemStart);
  547. #       else
  548.           VD_base = (char *)(XR_sysArea -> sa_sharedDataSeg.seg_addr);
  549. #       endif
  550.     }
  551.     VD_base += maxPageSize-1;
  552.     VD_base = (char *)((unsigned long) VD_base & ~(maxPageSize-1));
  553. }
  554.  
  555. void XR_EndVD()
  556. { }
  557.  
  558. void XR_ShutDownVD()
  559. { }
  560. # endif !DIRTY_BITS
  561.  
  562.  
  563. # ifdef DIRTY_BITS
  564. /* Get out of the way for shut down of the world     */
  565. /* Assumes that I am uninterruptible.            */
  566. /* THIS BREAKS THE WORLD !!                */
  567. /* It assumes that no subsequent GC will run to         */
  568. /* completion (which it wont if I'm uninterruptible).   */
  569. void XR_ShutDownVD()
  570. {
  571.     mprotect(VD_base, GC_heaplim - VD_base,
  572.          PROT_READ | PROT_WRITE | PROT_EXEC);
  573. }
  574.  
  575. /* Set dirty bits for a single region of memory.   */
  576. static int
  577. XR_VDSetDirty(vdb)
  578. XR_VDBuf vdb;
  579. {
  580.     register unsigned current = LOW_PAGE(vdb->vdb_seg);
  581.     register unsigned limit = HIGH_PAGE(vdb->vdb_seg);
  582.     
  583.     for (; current <= limit; current++) {
  584.         VD_Status[current] = VDS_DIRTY;
  585.         VDS_DELETE_CLIENT(current,VD_CLIENT_NO);
  586.     }
  587.     return(0);
  588. }
  589.  
  590.  
  591. long VD_clean_count = 0;  /* Number of requests to clean dirty bits */
  592.               /* Used for consistency checking.        */
  593.  
  594. /* Clear dirty bits for a single region of memory, and enable setting of */
  595. /* dirty bits in this region.                         */
  596. static int
  597. XR_VDSetClean(vdb)
  598. XR_VDBuf vdb;
  599. {
  600.     register unsigned start = LOW_PAGE(vdb->vdb_seg);
  601.     register unsigned top = HIGH_PAGE(vdb->vdb_seg);
  602.     register unsigned current;
  603.     int result;
  604.     
  605.     VD_clean_count++;
  606. #   if VDS_CLEAN == 0
  607.         bzero(VD_Status + start, top+1 - start);
  608. #   else
  609.         for (current = start; current <= top; current++) {
  610.             VD_Status[current] = VDS_CLEAN;
  611.         }
  612. #   endif
  613.     if((result = XR_ProtectHeap(VD_base + PAGES_TO_BYTES(start),
  614.                                  PAGES_TO_BYTES(top - start + 1),
  615.                                  VD_CLIENT_NO)) < 0) {
  616.         for (current = start; current <= top; current++) {
  617.             VD_Status[current] = VDS_DIRTY;
  618.         }
  619.         return(result);
  620.     } else {
  621.         return(0);
  622.     }
  623. }
  624.  
  625. /* Retrieve dirty bits for a single region of memory. */
  626. static int
  627. XR_VDGet(vdb)
  628. XR_VDBuf vdb;
  629. {
  630.     register char * vdb_bufp = vdb -> vdb_buf;
  631.     register unsigned current = LOW_PAGE(vdb->vdb_seg);
  632.     register unsigned top = HIGH_PAGE(vdb->vdb_seg);
  633.     
  634.     for (; current <= top; current++) {
  635.         *vdb_bufp++ = VD_Status[current];
  636.     }
  637.     return(0);
  638. }
  639.  
  640. /* Clear and retrieve dirty bits for a single region in memory. Returns 0, or */
  641. /* -errno in the event of an error.                          */
  642. static int
  643. XR_VDSetCleanAndGet(vdb)
  644. XR_VDBuf vdb;
  645. {
  646.     register char * vdb_bufp = vdb -> vdb_buf;
  647.     register unsigned start = LOW_PAGE(vdb->vdb_seg);
  648.     register unsigned top = HIGH_PAGE(vdb->vdb_seg);
  649.     register unsigned current;
  650.     register int result;
  651.  
  652.     VD_clean_count++;
  653.     /* Clear the user supplied buffer */
  654.         for (current = start; current <= top; current++) {
  655.             *vdb_bufp++ = VDS_CLEAN;
  656.         }
  657.         
  658.     /* Redirect dirty bit updates in the right region to user */
  659.     /* supplied buffer.                          */
  660.         vDCurrentBuf = vdb;
  661.         
  662.     /* Transfer bits set in VD_Status to client buffer.  */
  663.         vdb_bufp = vdb -> vdb_buf;
  664.         for (current = start; current <= top; current++) {
  665.         if (VD_Status[current] == VDS_DIRTY) {
  666.                 *vdb_bufp = VDS_DIRTY;
  667.             }
  668.             vdb_bufp++;
  669.         }
  670.         
  671.     /* Clear affected bits in VD_Status.            */
  672.         for (current = start; current <= top; current++) {
  673.             VD_Status[current] = VDS_CLEAN;
  674.         }
  675.         
  676.     /* Redirect dirty bit updates to their standard place */
  677.         vDCurrentBuf = 0;
  678.         
  679.     /* Reprotect affected pages. */
  680.     /* This involves sending signals, and thus involves ensuring */
  681.     /* that any signal handlers that saw the old vDCurrentBuf    */
  682.     /* will finish before we return.  This step should probably  */
  683.     /* be combined for all vdb's passed to the original XR_VDOp  */
  684.     /* call.                             */
  685.         if((result = XR_ProtectHeap(VD_base + PAGES_TO_BYTES(start),
  686.                                     PAGES_TO_BYTES(top - start + 1),
  687.                                     VD_CLIENT_NO)) < 0) {
  688.             return(result);
  689.         } else {
  690.             return(0);
  691.         }   
  692. }
  693.  
  694. /* Check a segment descriptor for validity. Returns 0 or -errno. */
  695. static int XR_SegValid(segptr)
  696. XR_Seg segptr;
  697. {
  698.     if (segptr -> seg_addr < (XR_Pointer)VD_base) {
  699.         return(-EINVAL);
  700.     }
  701.     if (segptr -> seg_addr  + segptr -> seg_bytes > (XR_Pointer)GC_heaplim) {
  702.         return(-EINVAL);
  703.     }
  704.     return(0);
  705. }
  706.  
  707. /* Check whether [start, start+len) overlaps [VD_start, GC_heaplim). */
  708. bool XR_OverlapsVDProtected(start, len)
  709. XR_Pointer start;
  710. unsigned long len;
  711. {
  712.     if (((unsigned long)start) + len <= (unsigned long) VD_base) {
  713.         return(FALSE);
  714.     }
  715.     if (((unsigned long)start) >= ((unsigned long)(GC_heaplim))) {
  716.         return(FALSE);
  717.     }
  718.     return(TRUE);
  719. }
  720.  
  721. # endif DIRTY_BITS
  722.  
  723. # ifndef DIRTY_BITS
  724. bool XR_OverlapsVDProtected(start, len)
  725. XR_Pointer start;
  726. unsigned long len;
  727. {
  728.     return(FALSE);
  729. }
  730. # endif DIRTY_BITS
  731.  
  732. # ifdef DIRTY_BITS
  733. /* Perform virtual dirty bit operations.  See GCVirtualDirty.h for     */
  734. /* details.  Currently uses above routines to do all the work a segment */
  735. /* at a time.  This may need to be improved.                */
  736. int
  737. XR_VDOp(op, nvdb, vdb )
  738. XR_VDOpCode op;
  739. int nvdb;
  740. XR_VDBuf vdb;
  741. {
  742.     static bool VD_first_time = TRUE;
  743.     register int i;
  744.     register int result;
  745.     bool acquired_lock = FALSE;
  746.     
  747.     /* We acquire vDWrite_ml no matter what, to ensure the consistency    */
  748.     /* of the returned data.  Occasionally such consistency isn't needed, */
  749.     /* but ...                                  */
  750.     if (XR_holderOfVDWriteLock != XR_currThread) {
  751.             XR_AcquireVDWriteLock();
  752.             acquired_lock = TRUE;
  753.     }
  754.  
  755.     if (!vDStarted) {
  756.         XR_StartVD();
  757.     }
  758.  
  759.     for (i = 0; i < nvdb; i++) {
  760.         if ((result = XR_SegValid(&(vdb[i].vdb_seg))) < 0) {
  761.             return(result);
  762.         }
  763.         if (vdb[i].vdb_buf == 0) {
  764.             switch(op) {
  765.               case XR_VDOpCodeNOOP:
  766.                 result = 0;
  767.                 break;
  768.               case XR_VDOpCodeSetDirty:
  769.                 result = XR_VDSetDirty(vdb+i);
  770.                 break;
  771.               case XR_VDOpCodeSetClean:
  772.                 result = XR_VDSetClean(vdb+i);
  773.                 break;
  774.             }
  775.         } else {
  776.             switch(op) {
  777.               case XR_VDOpCodeNOOP:
  778.                 result = XR_VDGet(vdb+i);
  779.                 break;
  780.               case XR_VDOpCodeSetDirty:
  781.                 if ((result = XR_VDGet(vdb+i)) < 0) break;
  782.                 result = XR_VDSetDirty(vdb+i);
  783.                 break;
  784.               case XR_VDOpCodeSetClean:
  785.                 result = XR_VDSetCleanAndGet(vdb+i);
  786.                 break;
  787.             }
  788.         }
  789.         if (result < 0) {
  790.             if (acquired_lock) {
  791.                 XR_ReleaseVDWriteLock();
  792.             }
  793.             return(result);
  794.         }
  795.     }
  796.     if (acquired_lock) {
  797.         XR_ReleaseVDWriteLock();
  798.     }
  799.     return(0);
  800. }
  801.  
  802. /* Dumb out of line procedure used to touch a page.  If this were in-line,  */
  803. /* it's too likely the optimizer would remove the store, which would defeat */
  804. /* the purpose.                                     */
  805. static void set(p, c)
  806. char *p;
  807. char c;
  808. {
  809.     *p = c;
  810. }
  811.  
  812. /* Write to all pages overlapping [start, start+len).  In unlikely     */
  813. /* cases, this may be visible to a concurrent thread.                */
  814. /* Otherwise, data is not modified.                    */
  815. void
  816. XR_Touch(start, len)
  817. XR_Pointer start;
  818. unsigned long len;
  819. {
  820.     register /* volatile */ char * i;
  821.     register XR_Pointer lim = (XR_Pointer)(((unsigned long) start) + len);
  822.  
  823.     for (i = (char *)start; i < (char *)lim; i += realPageSize) {
  824.         set(i, *i);
  825.     }
  826.     if (len > 0) {
  827.         i = (char *)(lim-1);
  828.         set(i, *i);
  829.     }
  830.     return;
  831. }
  832.  
  833. /* Similar to XR_Touch, but ensure that the "write" affects no data    */
  834. /* even with other concurrently executing threads.  This involves    */
  835. /* explicitly unpreotecting pages on this processor and setting the     */
  836. /* corresponding dirty bits. SLOW.                    */
  837. /* Assumes I hold vDWrite_ml.                        */
  838. void
  839. XR_TouchSafely(start, len)
  840. XR_Pointer start;
  841. unsigned long len;
  842. {
  843.     register XR_Pointer start_addr = start & ~(maxPageSize-1);
  844.     register XR_Pointer end_addr = (start+len + maxPageSize-1)
  845.                        & ~(maxPageSize-1);
  846.     register int i = INDX_FROM_ADDR(start_addr);
  847.     register int j = INDX_FROM_ADDR(end_addr);
  848.     register int k;
  849.     register int result;
  850.     
  851.     if (XR_holderOfVDWriteLock != XR_currThread) {
  852.         XR_Panic("XR_TouchSafely 0");
  853.     }
  854.     for (k = i; k <= j; k++) {
  855.         VD_Status[k] = VDS_DIRTY;
  856.     }
  857.     if ((result = XR_UnprotectHeap(start_addr, end_addr - start_addr,
  858.                        VD_CLIENT_NO)) != 0) {
  859.         XR_ConsoleMsg(
  860.               "Mprotect failure in XR_PrepareForWriting %d(a %d, s %d)\n",
  861.               -result, start_addr, end_addr - start_addr);
  862.         XR_Panic("XR_TouchSafely 1");
  863.     } 
  864.  
  865. }
  866.  
  867. /* Data structure used to remember info between the two halves of system call */
  868. /* wrappers.  Protected by vDWrite_ml.                          */
  869. # define MAXSEGMENTS 10  /* Maximum number of segments to be touched */
  870. static int nSegments;
  871. struct VD_SegRep {
  872.     XR_Pointer vds_addr;
  873.     unsigned long vds_bytes;
  874.     bool vds_safely;  /* May not actually be written,  handle with care */
  875. };
  876.  
  877. static struct VD_SegRep sCSegs[MAXSEGMENTS];
  878.  
  879. /*
  880.  * Add a segment to segment list and temporarily unprotect it in this VP.
  881.  * It is not necessary that addr be page aligned.
  882.  * Noop if no write protection fault handlers are registered.
  883.  */
  884. static void XR_GeneralUnprotectSeg();
  885.  
  886. void XR_UnprotectSeg(addr,len)
  887. XR_Pointer addr;
  888. unsigned long len;
  889. {
  890.     if (XR_numberOfProtVClients == 0) {
  891.         /* Nobody cares , nothing should be protected... */
  892.         return;
  893.     } else if (XR_numberOfProtVClients == 1 && vDStarted) {
  894.         /* Just faulting the page is sufficient, since we know it wont */
  895.         /* be reprotected.                           */
  896.         XR_Touch(addr,len);
  897.         return;
  898.     }
  899.     XR_GeneralUnprotectSeg(addr, len, FALSE);
  900. }
  901.  
  902. /* Similar to the above, but segment may not really be written.    */
  903. /* Note that the segment will appear to not have been written   */
  904. /* to other protection clients, such as data breakpoints.    */
  905. void XR_UnprotectSegSafely(addr,len)
  906. XR_Pointer addr;
  907. unsigned long len;
  908. {
  909.     if (XR_numberOfProtVClients == 0) {
  910.         /* Nobody cares , nothing should be protected... */
  911.         return;
  912.     } else if (XR_numberOfProtVClients == 1 && vDStarted) {
  913.         /* Just faulting the page is sufficient, since we know it wont */
  914.         /* be reprotected.                           */
  915.         XR_TouchSafely(addr,len);
  916.         return;
  917.     }
  918.     XR_GeneralUnprotectSeg(addr, len, TRUE);
  919. }
  920.     
  921. static void XR_GeneralUnprotectSeg(orig_addr,orig_len,safely)
  922. XR_Pointer orig_addr;
  923. long orig_len;
  924. bool safely;
  925. {
  926.     XR_Pointer rounded_addr;
  927.     long rounded_len;
  928.     
  929.     if ((unsigned long)orig_addr < (unsigned long)VD_base) {
  930.         orig_len -= (unsigned long)VD_base - (unsigned long)orig_addr;
  931.         orig_addr = (XR_Pointer)VD_base;
  932.     } 
  933.     rounded_addr = orig_addr & ~(realPageSize-1);
  934.  
  935.     if (! I_HOLD_ML(&vDWrite_ml)) {
  936.         XR_Panic("XR_UnprotectSeg 0");
  937.     }
  938.     if (orig_len <= 0
  939.         || (unsigned long)orig_addr >= (unsigned long)GC_heaplim) return;
  940.     if ((unsigned long)orig_addr + orig_len > (unsigned long)GC_heaplim) {
  941.         XR_Panic("XR_UnprotectSeg 1");
  942.     }
  943.     rounded_len = (((unsigned long)orig_addr + orig_len + realPageSize-1)
  944.                    & ~(realPageSize-1)) - (unsigned long)rounded_addr;
  945.     
  946.     sCSegs[nSegments].vds_addr = orig_addr;
  947.     sCSegs[nSegments].vds_bytes = orig_len;
  948.     sCSegs[nSegments].vds_safely = safely;
  949.     nSegments++;
  950.     if (mprotect(rounded_addr, rounded_len,
  951.                  PROT_READ | PROT_EXEC | PROT_WRITE) < 0) {
  952.         XR_ConsoleMsg("XR_UnprotectSeg : addr = 0x%X, len = 0x%X\n",
  953.                   rounded_addr, rounded_len);
  954.         XR_Panic("XR_UnprotectSeg 1");
  955.     }
  956. }
  957.  
  958. /* Reprotect pages that should be protected in this VP and touch all */
  959. /* pages involved.  (Just setting dirty bits is not enough, since    */
  960. /* there may be other clients for protection faults.)             */
  961. static void ReprotectSegs()
  962. {
  963.     register int i;
  964.     XR_Pointer orig_addr;
  965.     long orig_len;
  966.  
  967.  
  968.     for (i = 0; i < nSegments; i++) {
  969.         if (! I_HOLD_ML(&vDWrite_ml)) {
  970.             XR_Panic("XR_ReprotectSegs 0");
  971.         }
  972.         orig_addr = sCSegs[i].vds_addr;
  973.         orig_len = sCSegs[i].vds_bytes;
  974.         /* Set dirty bits, clear client bit */
  975.         {
  976.             register XR_Pointer start_addr = orig_addr & ~(maxPageSize-1);
  977.             register XR_Pointer end_addr = (orig_addr + orig_len
  978.                                  + maxPageSize-1)
  979.                                  & ~(maxPageSize-1);
  980.             register int i = INDX_FROM_ADDR(start_addr);
  981.         register int j = INDX_FROM_ADDR(end_addr);
  982.         register int k;
  983.  
  984.         for (k = i; k <= j; k++) {
  985.             VD_Status[k] = VDS_DIRTY;
  986.             VDS_DELETE_CLIENT(k, VD_CLIENT_NO);
  987.             }
  988.         }
  989.         /* Reprotect pages with other clients */
  990.            Reprotect(orig_addr, orig_len);
  991.     }
  992.     for (i = 0; i < nSegments; i++) {
  993.       if (!sCSegs[i].vds_safely) {
  994.         /* This may not invoke data breakpoint handler, since the     */
  995.         /* breakpoint could be set in the middle, and we only touch    */
  996.         /* limits and page boundaries.    At least we try.        */
  997.         XR_Touch(sCSegs[i].vds_addr, sCSegs[i].vds_bytes);
  998.       }
  999.     }
  1000. }
  1001.  
  1002. /*
  1003.  * Lock myself to current VP, and ensure that addresses in [start, start+len)
  1004.  * are not write protected. Lock out calls to XR_VDOp that might reprotect these
  1005.  * pages by acquiring VDWriteLock.  Must be followed by a call to
  1006.  * XR_UnprotectSysCall, otherwise things will wedge.
  1007.  * We assume that I do not already hold VDWriteLock.  Thus this should
  1008.  * never be invoked with the world stopped.
  1009.  * System calls protected in this way may be preceded by additional calls
  1010.  * to XR_UnprotectSeg, if they write to more than one area of memory.
  1011.  */
  1012.  void
  1013.  XR_ProtectSysCall(start, len)
  1014.  XR_Pointer start;
  1015.  unsigned long len;
  1016.  {
  1017.     int ans, err;
  1018.     unsigned long myProcessor;
  1019.  
  1020. #   define CHECKANS(i) if(ans < 0) { err = i; goto Bad; }
  1021.  
  1022.     if (XR_holderOfVDWriteLock == XR_currThread) {
  1023.         XR_Panic("XR_ProtectSysCall 0");
  1024.     }
  1025.     XR_AcquireVDWriteLock();
  1026.     
  1027.     if (XR_numberOfProtVClients == 0) {
  1028.         return;
  1029.     } 
  1030.     ans = XR_SchedCtl(
  1031.             XR_SchedCtlWhichSelf(),
  1032.             scop_getProcessor,
  1033.             &myProcessor
  1034.         );
  1035.     CHECKANS(1);
  1036.     ans = XR_SchedCtl(
  1037.             XR_SchedCtlWhichSelf(),
  1038.             scop_setProcessor,
  1039.             &myProcessor
  1040.         );
  1041.     CHECKANS(2);
  1042.     ans = XR_SchedCtlWait();
  1043.     CHECKANS(3);
  1044.     
  1045.     if (XR_currThread->t_swStat != XR_SWSTAT_NONE) {
  1046.         XR_ConsoleMsg("XR_ProtectSysCall: switch state = %d!!\n",
  1047.         XR_currThread->t_swStat);
  1048.     }
  1049.     XR_currThread->t_swStat = XR_SWSTAT_DONT_PREEMPT;
  1050.     
  1051.     nSegments = 0;
  1052.     XR_UnprotectSeg(start, len);
  1053.         
  1054.     return;
  1055.  
  1056.   Bad:
  1057.     XR_ConsoleMsg("%? schedctl ans %d err %d\n", ans, err);
  1058.     XR_Panic("XR_ProtectSysCall 1");
  1059. #   undef CHECKANS
  1060.  
  1061.  
  1062.  }
  1063.  
  1064. /*
  1065.  * Unlock myself from any specific VP and release the gc trace lock
  1066.  */
  1067. void 
  1068. XR_UnprotectSysCall()
  1069.  {
  1070.     int ans, err;
  1071.     unsigned long anyProcessor = scop_processorArgAny;
  1072.     int old_errno = XR_GetErrno();
  1073.     
  1074. #   define CHECKANS(i) if(ans < 0) { err = i; goto Bad; }
  1075.  
  1076.     if (XR_numberOfProtVClients > 0) {
  1077.         ReprotectSegs();
  1078.  
  1079.         XR_currThread->t_swStat = XR_SWSTAT_NONE;
  1080.  
  1081.         ans = XR_SchedCtl(
  1082.                 XR_SchedCtlWhichSelf(),
  1083.                 scop_setProcessor,
  1084.                 &anyProcessor
  1085.              );
  1086.         CHECKANS(1);
  1087.         
  1088.         XR_SetErrno(old_errno);
  1089.     }
  1090.     
  1091.     XR_ReleaseVDWriteLock();
  1092.     
  1093.     XR_CheckReschedRequest();
  1094.     
  1095.     return;
  1096.  
  1097.   Bad:
  1098.     XR_ConsoleMsg("%? schedctl ans %d err %d\n", ans, err);
  1099.     XR_Panic("XR_UnprotectSysCall");
  1100. #   undef CHECKANS
  1101.  
  1102. }
  1103.  
  1104. # endif DIRTY_BITS
  1105.  
  1106. # ifndef DIRTY_BITS
  1107. void
  1108. XR_ProtectSysCall(start, len)
  1109. XR_Pointer start;
  1110. unsigned long len;
  1111. {}
  1112.  
  1113. void XR_UnprotectSysCall()
  1114. {}
  1115.  
  1116. void
  1117. XR_UnprotectSeg(start, len)
  1118. XR_Pointer start;
  1119. unsigned long len;
  1120. {}
  1121.  
  1122. void
  1123. XR_UnprotectSegSafely(start, len)
  1124. XR_Pointer start;
  1125. unsigned long len;
  1126. {}
  1127. # endif DIRTY_BITS
  1128.